/* 
 * UrlyBirdController.java 26.12.2011
 *  
 *  
 * Candidate: Alessandra Otterbach
 * Prometric ID: Algisina1
 * Username: Algisina
 *  
 * Sun Certified Developer for Java 2 Platform, Standard Edition Programming 
 * Assignment (CX-310-252A)
 *  
 * This class is part of the Programming Assignment of the Sun Certified 
 * Developer for Java 2 Platform, Standard Edition certification program, must 
 * not be used out of this context and must be used exclusively by Sun 
 * Microsystems, Inc. 
 */
package suncertify.ps;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.RemoteException;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;

import suncertify.rm.UrlyBirdReg;
import suncertify.util.PropertiesHelper;
import suncertify.util.StringUtils;

/**
 * Controller for all the activity for the UrlyBirdMainView. Has the logic for
 * ok, start and exit button and for the fileChooser. Does some checking for the
 * input parameters.
 * 
 * @author Alessandra Otterbach
 * 
 */
public class UrlyBirdMainController {

    private UrlyBirdMainView view;

    private ApplicationRunMode mode;

    public UrlyBirdMainController() {
	this.view = new UrlyBirdMainView();

	addListener();
    }

    public UrlyBirdMainController(ApplicationRunMode mode) {
	this.view = new UrlyBirdMainView(mode);
	this.mode = mode;

	addListener();
    }

    public void showView() {
	this.view.setVisible(true);
    }

    /**
     * Let the view know everything about the listeners, so that the view can
     * communicate with the controller
     * 
     */
    private void addListener() {
	this.view.setFileChooserListener(new FileChooserListener());
	this.view.setOkListener(new OkListener());
	this.view.setServerStartListener(new ServerStartListener());
	this.view.setExitListener(new ExitListener());
    }

    /**
     * Listener for the ok button. Stores the properties, checks ip and port.
     * Calls BookingController to show the BookingView.
     */
    class OkListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {

	    if (mode != null && ApplicationRunMode.NETWORK_CLIENT.equals(mode)) {
		// Save Properties
		PropertiesHelper.storeProperties(null, view.getTxtIp(),
			view.getTxtPort());
		try {

		    // Check Ip and port number
		    if (checkIp(view.getTxtIp())
			    && checkPortNumber(view.getTxtPort())) {

			view.dispose();
			new BookingController(view.getTxtIp(),
				view.getTxtPort(), mode);
		    }
		} catch (UnknownHostException uhe) {
		    GuiExceptionHandler
			    .handleGuiException("UnknownHostException. Please reenter ip address.");
		    view.getButtonOk().setEnabled(true);
		}
	    } else {
		// Check if path isn't empty
		if (StringUtils.isEmpty(view.getTxtFileName())) {
		    GuiExceptionHandler
			    .handleGuiException("Please choose a path.");
		} else {
		    Thread exitRoutine = new CleanShutdown(
			    view.getTxtFileName());
		    Runtime.getRuntime().addShutdownHook(exitRoutine);
		    // Save Properties
		    PropertiesHelper.storeProperties(view.getTxtFileName(),
			    null, null);
		    view.dispose();
		    new BookingController(view.getTxtFileName(), mode);
		}
	    }
	}
    }

    /**
     * Listener for the filechooser. Only shows directories and files with the ending *.db
     */
    class FileChooserListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {

	    view.getFileChooser().setFileFilter(new FileFilter() {
		public boolean accept(File f) {
		    return f.getName().toLowerCase().endsWith(".db")
			    || f.isDirectory();
		}

		public String getDescription() {
		    return "(*.db)";
		}
	    });

	    if (StringUtils.isNotEmpty(view.getTxtFileName())) {
		view.setSelectedFile(new File(view.getTxtFileName()));
	    }
	    int state = view.getFileChooser().showOpenDialog(null);
	    if (JFileChooser.APPROVE_OPTION == state) {
		view.setTxtFileName(view.getFileChooser().getSelectedFile()
			.getAbsolutePath());
	    }
	}
    }

    /**
     * ActionListener that starts the RMI Server. Checks the port number before.
     */
    class ServerStartListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    // Save Properties
	    PropertiesHelper.storeProperties(view.getTxtFileName(), null,
		    view.getTxtPort());
	    try {
		// Check if path isn't empty
		if (StringUtils.isEmpty(view.getTxtFileName())) {
		    GuiExceptionHandler
			    .handleGuiException("Please choose a path.");
		} else {
		    if (checkPortNumber(view.getTxtPort())) {
			try {
			    view.getButtonServer().setEnabled(false);
			    UrlyBirdReg.register(view.getTxtFileName(),
				    Integer.parseInt(view.getTxtPort()));
			    Thread exitRoutine = new CleanShutdown(
				    view.getTxtFileName());
			    Runtime.getRuntime().addShutdownHook(exitRoutine);
			} catch (RemoteException exc) {
			    GuiExceptionHandler
				    .handleGuiException("Error when trying to start the server. Please check, if the server is already running. Systemmessage: "
					    + exc.getMessage());
			    System.exit(0);
			} catch (IOException ioe) {
			    GuiExceptionHandler
				    .handleGuiException("Error during file handling. Please check file path or restart application. Systemmessage: "
					    + ioe.getMessage());
			    System.exit(0);
			}
		    }
		}
	    } catch (NumberFormatException nfe) {
		GuiExceptionHandler
			.handleGuiException("Port must be a number. Please reenter port number.");
	    }
	}
    }

    /**
     * ActionListener that stops the RMI Server and closes the gui
     */
    class ExitListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    System.exit(0);
	}
    }

    /**
     * Checks if port number is in the range between 1 an 65535.
     * 
     * @param portnumber
     * @return true, if port number is in the range
     * @throws NumberFormatException
     *             if port number is not a number.
     */
    private boolean checkPortNumber(String port) throws NumberFormatException {

	if (StringUtils.isNotEmpty(port)) {
	    int portnumber = Integer.parseInt(port.trim());
	    // Rangecheck
	    if (portnumber < 1 || portnumber > 65535) {
		GuiExceptionHandler
			.handleGuiException("Port must be in the range 1 - 65535. Please reenter port number.");
		return false;
	    }
	    return true;
	} else {
	    GuiExceptionHandler
		    .handleGuiException("Please enter a port number.");
	    return false;
	}

    }

    /**
     * Checks, if ip is empty, is a valid ip address or hostname.
     * 
     * @param ip
     *            to be checked
     * @return true, if all checks are true
     * @throws UnknownHostException
     */
    private boolean checkIp(String ip) throws UnknownHostException {

	if (StringUtils.isEmpty(ip)) {
	    GuiExceptionHandler
		    .handleGuiException("Ip must not be empty. Please reenter ip address.");
	    return false;
	}
	// Ip address
	if (Character.isDigit(ip.charAt(0))) {
	    if (!validateIpAdress(ip)) {
		throw new UnknownHostException("Unknown Host.");
	    }
	} else {
	    // Hostname
	    InetAddress.getAllByName(ip);
	}

	return true;

    }

    /**
     * Validates the ip address, so that its format is xxx.xxx.xxx.xxx, with xxx
     * being a number between 1 and 255.
     * 
     * @param ip
     *            ip address to be validated
     * @return true, if it is a valid ip address
     */
    private boolean validateIpAdress(String ip) {
	String[] parts = ip.split("\\.");

	if (parts.length != 4) {
	    return false;
	}

	for (String s : parts) {
	    try {
		int i = Integer.parseInt(s);

		if ((i < 0) || (i > 255)) {
		    return false;
		}
	    } catch (NumberFormatException e) {
		return false;
	    }
	}

	return true;
    }
}
